using System.Collections.Concurrent;
using Consul;
using Aurora.Discovery.HttpClient.Abstractions;
using Discovery.HttpClient.Consul.Options;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Options;

namespace Discovery.HttpClient.Consul;

/// <summary>
///  consul discovery client 
/// </summary>
public class ConsulDiscoveryClient : IDiscoveryClient
{
    private readonly ConsulDiscoveryOptions _options;
    private readonly Timer _timer;
    private readonly ConsulClient _consulClient;
    private readonly string _serviceIdInConsul;
    private static readonly Random _random = new Random((int)DateTime.Now.Ticks);
    private bool _useDiscovery = true;

    public ConcurrentDictionary<string, List<string>> AllServices { get; private set; } =
        new ConcurrentDictionary<string, List<string>>();

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="options"></param>
    public ConsulDiscoveryClient(IOptions<ConsulDiscoveryOptions> options)
    {
        _options = options.Value;
        // 证明不启用 那么就不需要注册了
        if (_options == null)
        {
            _useDiscovery = false;
        }
        else
        {
            _consulClient = new ConsulClient(x =>
            {
                x.Address = new Uri(
                    $"http://{_options.ConsulServerSetting.IP}:{_options.ConsulServerSetting.Port}");
            });
            _timer = new Timer(Refresh);

            if (_options?.ServiceRegisterSetting != null)
            {
                _serviceIdInConsul = Guid.NewGuid().ToString();
            }
        }
    }

    /// <summary>
    /// 开启服务
    /// </summary>
    /// <exception cref="ArgumentException"></exception>
    public void Start()
    {
        if (!_useDiscovery) return;
        var checkErrorMsg = CheckParams();
        if (checkErrorMsg != null)
        {
            throw new ArgumentException(checkErrorMsg);
        }

        RegisterToConsul();
        _timer.Change(0, _options.ConsulServerSetting.RefreshIntervalInMilliseconds);
    }

    /// <summary>
    /// 停止服务
    /// </summary>
    public void Stop()
    {
        if (!_useDiscovery) return;
        Dispose();
    }

    public void Use(IApplicationBuilder app)
    {
        
    }


    /// <summary>
    /// 检查参数
    /// </summary>
    /// <returns></returns>
    private string CheckParams()
    {
        if (string.IsNullOrWhiteSpace(_options.ConsulServerSetting.IP))
        {
            return "Consul服务器地址 ConsulDiscoveryOptions.ConsulServerSetting.IP 不能为空";
        }

        if (_options?.ServiceRegisterSetting != null)
        {
            var registerSetting = _options.ServiceRegisterSetting;
            if (string.IsNullOrWhiteSpace(registerSetting.ServiceName))
            {
                return "服务名称 ConsulDiscoveryOptions.ServiceRegisterSetting.ServiceName 不能为空";
            }

            if (string.IsNullOrWhiteSpace(registerSetting.ServiceIP))
            {
                return "服务地址 ConsulDiscoveryOptions.ServiceRegisterSetting.ServiceIP 不能为空";
            }
        }

        return null;
    }

    /// <summary>
    /// 注册服务到 consul中
    /// </summary>
    private void RegisterToConsul()
    {
        if (string.IsNullOrEmpty(_serviceIdInConsul))
        {
            return;
        }

        var registerSetting = _options.ServiceRegisterSetting;
        var httpCheck = new AgentServiceCheck()
        {
            HTTP =
                $"{registerSetting.ServiceScheme}{Uri.SchemeDelimiter}{registerSetting.ServiceIP}:{registerSetting.ServicePort}/{registerSetting.HealthCheckRelativeUrl.TrimStart('/')}",
            Interval = TimeSpan.FromMilliseconds(registerSetting.HealthCheckIntervalInMilliseconds),
            Timeout = TimeSpan.FromMilliseconds(registerSetting.HealthCheckTimeOutInMilliseconds),
            DeregisterCriticalServiceAfter = TimeSpan.FromSeconds(10),
        };
        var registration = new AgentServiceRegistration()
        {
            ID = _serviceIdInConsul,
            Name = registerSetting.ServiceName,
            Address = registerSetting.ServiceIP,
            Port = registerSetting.ServicePort,
            Check = httpCheck,
            Meta = new Dictionary<string, string>() { ["scheme"] = registerSetting.ServiceScheme },
        };
        _consulClient.Agent.ServiceRegister(registration).Wait();
    }

    /// <summary>
    /// 关闭服务时 释放consul的实例
    /// </summary>
    private void DeregisterFromConsul()
    {
        if (string.IsNullOrEmpty(_serviceIdInConsul))
        {
            return;
        }

        try
        {
            _consulClient.Agent.ServiceDeregister(_serviceIdInConsul).Wait();
        }
        catch
        {
        }
    }

    /// <summary>
    /// 刷新缓存
    /// </summary>
    /// <param name="state"></param>
    private void Refresh(object state)
    {
        Dictionary<string, AgentService>.ValueCollection serversInConsul;
        try
        {
            serversInConsul = _consulClient.Agent.Services().Result.Response.Values;
        }
        catch // (Exception ex)
        {
            // 如果连接consul出错, 则不更新服务列表. 继续使用以前获取到的服务列表
            // 但是如果很长时间都不能连接consul, 服务列表里的一些实例已经不可用了, 还一直提供这样旧的列表也不合理, 所以要不要在这里实现 健康检查? 这样的话, 就得把检查地址变成不能设置的
            return;
        }

        // 1. 更新服务列表
        // 2. 如果这个程序提供了服务, 还要检测 服务Id 是否在服务列表里
        var tempServices = new Dictionary<string, HashSet<string>>();
        bool needReregisterToConsul = true;
        foreach (var service in serversInConsul)
        {
            var serviceName = service.Service;
            if (!service.Meta.TryGetValue("scheme", out var serviceScheme))
            {
                serviceScheme = Uri.UriSchemeHttp;
            }

            var serviceHost = $"{serviceScheme}{Uri.SchemeDelimiter}{service.Address}:{service.Port}";
            if (!tempServices.TryGetValue(serviceName, out var serviceHosts))
            {
                serviceHosts = new HashSet<string>();
                tempServices[serviceName] = serviceHosts;
            }

            serviceHosts.Add(serviceHost);

            if (needReregisterToConsul && !string.IsNullOrEmpty(_serviceIdInConsul) && _serviceIdInConsul == service.ID)
            {
                needReregisterToConsul = false;
            }
        }

        if (needReregisterToConsul)
        {
            RegisterToConsul();
        }

        var tempAllServices = new ConcurrentDictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
        foreach (var item in tempServices)
        {
            tempAllServices[item.Key] = item.Value.ToList();
        }

        AllServices = tempAllServices;
    }

    public void Dispose()
    {
        if (!_useDiscovery) return;
        DeregisterFromConsul();
        _consulClient?.Dispose();
        _timer?.Dispose();
    }


    /// <summary>
    /// 获取服务
    /// </summary>
    /// <param name="host"></param>
    /// <param name="serviceHost"></param>
    /// <returns></returns>
    public async Task<string> GetService(string host)
    {
        if (AllServices.TryGetValue(host, out var tmpHost))
        {
            if (tmpHost.Count > 0)
            {
                var index = _random.Next(tmpHost.Count);
                var serviceHost = tmpHost[index];
                return serviceHost;
            }
        }

        return "";
    }
}